﻿//////////////////////////////////////////////////////////////////
//
// Convoy.cs
//
// Copyright (c) 2012 Dan Pike. All rights reserved
//
// Refer to license.txt for any license restrictions.
// 
using Logger4Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utilities;

namespace Entities
{
   public class Convoy : Entity
   {
      //////////////////////////////////////////////////////////////////////////
      //
      public Convoy(World world)
         : base(world)
      {
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public override string name
      {
         get { return base.name;  }
         set
         {
            try
            {
               // Name or rename the convoy
               if (!string.IsNullOrWhiteSpace(base.name))
               {
                  world.convoys.Remove(base.name);
               }
               base.name = value.Trim();
               if (!string.IsNullOrWhiteSpace(base.name))
               {
                  world.convoys.Add(base.name, this);
               }
            }
            catch (System.Exception ex)
            {
               logger_.exception(ex);
            }
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public override string ToString()
      {
         return name;
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public double roundTripTime
      {
         get
         {
            double duration = 0.0;
            try
            {
               int portCount = ports_.Count;
               for (int index = 0; index < portCount; ++index)
               {
                  duration += world.getJourneyTime(ports_[index], ports_[(index + 1) % portCount]);
                  duration += 1.0;  // OPne day in each port
               }
            }
            catch (System.Exception ex)
            {
               logger_.exception(ex);
            }
            return duration;
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public void clear()
      {
         try
         {
            ports_.Clear();
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public bool load(Archive.Convoy archiveConvoy)
      {
         try
         {
            clear();
            name = archiveConvoy.name;
            archiveConvoy.ports.OfType<Archive.Port>().All(port =>
               {
                  string portName = Global.normalizeName(port.name, Global.NormalizeType.City);
                  City city = world.cities.FirstOrDefault(qCity => qCity.name == portName);
                  if (null == city)
                  {
                     logger_.errorFormat("ignoring unknown city '{0}' in convoy '{1}'", city.name, port);
                  }
                  else
                  {
                     addPort(city);

                     if (archiveConvoy.hub == portName)
                     {
                        hub = city;
                     }
                  }
                  return true;
               });
            dirty = false;
            return true;
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
         return false;
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public bool write(Archive.Convoy archiveConvoy)
      {
         try
         {
            archiveConvoy.hub = (null != hub) ? hub.name : string.Empty;
            archiveConvoy.ports.Clear();
            ports_.ForEach(qPort =>
               {
                  Archive.Port archivePort = new Archive.Port();
                  archivePort.name = qPort.name;
                  archiveConvoy.ports.Add(archivePort);
               });
            dirty = false;
            return true;
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
         return false;
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public List<City> snapshotPorts()
      {
         return ports_.ToList();
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public City removePort(int index)
      {
         City result = null;
         try
         {
            result = ports_[index];
            ports_.RemoveAt(index);
            checkAllRemoved(result);
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
         return result;
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public void insertPort(int index, City port)
      {
         try
         {
            ports_.Insert(index, port);
            checkJustAdded(port);
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public void addPort(City port)
      {
         try
         {
            ports_.Add(port);
            checkJustAdded(port);
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      private void checkJustAdded(City port)
      {
         try
         {
            if (1 == ports_.Count(city => city == port))
            {
               port.addConvoy(this);
            }
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      private void checkAllRemoved(City port)
      {
         try
         {
            if (!ports_.Any(city => city == port))
            {
               port.removeConvoy(this);
            }
         }
         catch (System.Exception ex)
         {
            logger_.exception(ex);
         }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public City hub
      {
          get { return hub_; }
          set
          {
             if (hub_ != value)
             {
                hub_ = value;
                dirty = true;
             }
          }
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public override int GetHashCode() { return (int)HashCodes.Convoy; }

      //////////////////////////////////////////////////////////////////////////
      //
      private List<City> ports_ = new List<City>();
      private City hub_;
   }
}
